/**
 * 
 */
package br.ufpr.inf.jobtuner.pso;

import java.util.Random;

/**
 * @author Edson
 * @param
 */
public class PSOParticle {
	private PSOFitness fitness = new PSOFitness();
	private Position position = new Position();
	private Position best = new Position();
	private Velocity velocity = new Velocity();
	private static Random random = new Random();
	static double min = -2; 
	static double max = 2;

	public static class Position {
		private double x = 0;
		private double y = 0;

		public Position() {
			this.x = random.nextDouble();
			this.y = random.nextDouble();
		}

		public Position(double x, double y) {
			this.x = x;
			this.y = y;
		}

		public void setx(double x) {
			this.x = x;
		}

		public void sety(double y) {
			this.y = y;
		}

		public void setxy(double x, double y) {
			this.x = x;
			this.y = y;
		}

		public double getx() {
			return x;
		}

		public double gety() {
			return y;
		}
	}

	public static class Velocity {
		private double x;
		private double y;

		public Velocity() {
			this.x = random.nextDouble();
			this.y = random.nextDouble();
		}

		public Velocity(double x, double y) {
			this.x = x;
			this.y = y;
		}

		public void setx(double x) {
			this.x = x;
		}

		public void sety(double y) {
			this.y = y;
		}

		public void setxy(double x, double y) {
			this.x = x;
			this.y = y;
		}

		public double getx() {
			return x;
		}

		public double gety() {
			return y;
		}
	}

	public Velocity getVelocity() {
		return velocity;
	}

	public Velocity setVelocity(double x, double y) {
		velocity.setxy(x, y);
		return velocity;
	}

	public Velocity updateVelocity(double globalx, double globaly) {
		double phi1, phi2, inertia;
		
		phi1 = random.nextDouble();
		phi2 = random.nextDouble();
		inertia = random.nextDouble();
		velocity.x = inertia*velocity.x + (0.9*phi1*(best.x - position.x)) + (1.1*phi2*(globalx - position.x));
		velocity.x = (velocity.x > max)? max : velocity.x;
		velocity.x = (velocity.x < min)? min : velocity.x;
		
		phi1 = random.nextDouble();
		phi2 = random.nextDouble();
		inertia = random.nextDouble();
		velocity.y = inertia*velocity.y + (0.9*phi1*(best.y - position.y)) + (1.1*phi2*(globaly - position.y));
		velocity.y = (velocity.y > max)? max : velocity.y;
		velocity.y = (velocity.y < min)? min : velocity.y;
		return velocity;
	}

	public Position getPosition() {
		return position;
	}

	public Position setPosition(double x, double y) {
		position.setxy(x, y);
		return position;
	}

	public Position updatePosition() {
		position.x = position.x + velocity.x;
		position.x = (position.x > max)? max : position.x;
		position.x = (position.x < min)? min : position.x;
		
		position.y = position.y + velocity.y;
		position.y = (position.y > max)? max : position.y;
		position.y = (position.y < min)? min : position.y;
		return position;
	}

	public PSOParticle(double x, double y, double k, double w) {
		position.setxy(x, y);
		velocity.setxy(k, w);
	}

	public double calculateFitness() {
		return fitness.calculate(position, velocity);
	}

	public boolean updateBestFitness() {
		if (fitness.updateBestFitness()) {
			best.x = position.x;
			best.y = position.y;
			return true;
		}
		return false;
	}

	public double getFitness() {
		return fitness.getCurrentFitness();
	}

	public double getBestFitness() {
		return fitness.getBestFitness();
	}

	public Position getBestPosition() {
		return best;
	}

	public void updateInfo(double globalx, double globaly) {
		updateVelocity(globalx, globaly);
		updatePosition();
	}
}
